package com.wzxl.config.shiro;


import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.wzxl.utils.ConstantUtils;
import com.wzxl.utils.collection.MapUtil;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.Map;
/**
 * @author 马浩 mahao@aowlx.onaliyun.com
 * @Package com.wzxl.config.shiro
 * @Description: TODO ( Shiro配置 )
 * @Date 2018/4/20 16:56
 * @Version V1.0
 */
@Configuration
public class ShiroConfiguration {

    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
        daap.setProxyTargetClass(true);
        return daap;
    }

    /**
     * 身份认证 realm
     *
     * @return
     */
    @Bean(name = "shiroRealm")
    public DemoAuthorizingRealm demoAuthorizingRealm() {
        DemoAuthorizingRealm authorizingRealm = new DemoAuthorizingRealm();
        authorizingRealm.setCredentialsMatcher(demoHashedCredentialsMatcher());
        return authorizingRealm;
    }
    /**
     * 凭证匹配器,由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了,所以我们需要修改下doGetAuthenticationInfo中的代码;
     *
     * @return
     */
    @Bean(name = "demoHashedCredentialsMatcher")
    public HashedCredentialsMatcher demoHashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        /**
         * 散列算法:这里使用MD5算法;
         */
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        /**
         * 散列的次数，比如散列两次，相当于md5(md5(""));
         */
        hashedCredentialsMatcher.setHashIterations(2);
        return hashedCredentialsMatcher;
    }

    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager() {
        DefaultWebSecurityManager webSecurityManager = new DefaultWebSecurityManager();
        webSecurityManager.setRealm(demoAuthorizingRealm());
        webSecurityManager.setCacheManager(getCacheManager());
        webSecurityManager.setSessionManager(getSessionManager());
        return webSecurityManager;
    }

    @Bean
    public ShiroRedisCacheManager getCacheManager() {
        ShiroRedisCacheManager cacheManager = new ShiroRedisCacheManager();
        cacheManager.setKeyPrefix(ConstantUtils.SHIRO_REDIS_CACHE);
        return cacheManager;
    }

    @Bean(name = "sessionIdCookie")
    public Cookie getSessionIdCookie() {
        // cookie的name,对应的默认是 JSESSIONID
        SimpleCookie cookie = new SimpleCookie("MY_JSESSIONID");
        // jsessionId的path为 / 用于多个系统共享jsessionId
        cookie.setHttpOnly(true);
        cookie.setPath("/");
        return cookie;
    }
    @Bean
    public DefaultWebSessionManager getSessionManager() {
        CustomWebSessionManager sessionManager = new CustomWebSessionManager();
        // 设置全局会话超时时间，默认30分钟(1800000)
        sessionManager.setGlobalSessionTimeout(1800000);
        // 是否在会话过期后会调用SessionDAO的delete方法删除会话 默认true
        sessionManager.setDeleteInvalidSessions(true);
        // 会话验证器调度时间
        sessionManager.setSessionValidationInterval(1800000);
        // 自定义SessionDao
        sessionManager.setSessionDAO(getSessionDao());
        // 自定义Cookie
        sessionManager.setSessionIdCookie(getSessionIdCookie());
        // 定时检查失效的session
        sessionManager.setSessionValidationSchedulerEnabled(true);
        return sessionManager;
    }

    @Bean
    public SessionDAO getSessionDao() {
        ShiroRedisSessionDao sessionDAO = new ShiroRedisSessionDao();
        sessionDAO.setKeyPrefix(ConstantUtils.SHIRO_REDIS_SESSION);
        return sessionDAO;
    }

    /**
     * 开启shiro aop注解支持.
     *
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
        aasa.setSecurityManager(getDefaultWebSecurityManager());
        return new AuthorizationAttributeSourceAdvisor();
    }

    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean() {
        /**
         * anon（匿名）  org.apache.shiro.web.filter.authc.AnonymousFilter
         * authc（身份验证）       org.apache.shiro.web.filter.authc.FormAuthenticationFilter
         * authcBasic（http基本验证）    org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
         * logout（退出）        org.apache.shiro.web.filter.authc.LogoutFilter
         * noSessionCreation（不创建session） org.apache.shiro.web.filter.session.NoSessionCreationFilter
         * perms(许可验证)  org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
         * port（端口验证）   org.apache.shiro.web.filter.authz.PortFilter
         * rest  (rest方面)  org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
         * roles（权限验证）  org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
         * ssl （ssl方面）   org.apache.shiro.web.filter.authz.SslFilter
         * member （用户方面）  org.apache.shiro.web.filter.authc.UserFilter
         * user  表示用户不一定已通过认证,只要曾被Shiro记住过登录状态的用户就可以正常发起请求,比如rememberMe
         */
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        /**
         * 设置 SecurityManager
         */
        shiroFilterFactoryBean.setSecurityManager(getDefaultWebSecurityManager());

        /**
         * 增加自定义过滤
         */
        Map<String, Filter> filters = MapUtil.newHashMap();
        shiroFilterFactoryBean.setFilters(filters);

        /**
         *  拦截器
         */
        Map<String, String> filterChainDefinitionMap = MapUtil.newHashMap();

        /**
         *  <!-- 过滤链定义，从上向下顺序执行，一般将 /**放在最为下边-->
         *
         *  <!-- authc: 所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
         */
        filterChainDefinitionMap.put("/api/console/loginto", "anon");
        filterChainDefinitionMap.put("/**/**/login", "anon");

        /**
         * 配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
         */
        filterChainDefinitionMap.put("/**/**/logout", "logout");

        filterChainDefinitionMap.put("/api/**/**", "authc");
        filterChainDefinitionMap.put("/api/console/show", "anon");

        /**
         * 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
         */
        shiroFilterFactoryBean.setLoginUrl("/api/console/login");

        /**
         * 登录成功后要跳转的链接
         */
        shiroFilterFactoryBean.setSuccessUrl("/api/console/index");

        /**
         * 未授权界面
         */
        shiroFilterFactoryBean.setUnauthorizedUrl("/console/403");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }

    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
}
